/**
 * Copyright (c) 2005-2012 https://github.com/zhangkaitao
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package com.tyf.common.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tyf.common.dao.BaseDao;
import com.tyf.common.domain.AbstractEntity;
import com.tyf.common.vo.SystemUser;
import com.tyf.common.domain.search.Searchable;
import com.tyf.common.util.BizUtils;
import com.tyf.common.util.ReflectUtils;
import com.google.common.collect.Lists;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 *
 * @ClassName: BaseService<br>
 * @Description: 基础服务类<br>
 * @author xd<br>
 * @date Jul 28, 2016 6:07:37 PM<br>
 *
 * @param <M>
 * @param <ID>
 */
@SuppressWarnings({"unchecked"})
public abstract class BaseService<M extends AbstractEntity<?>, ID extends Serializable> {
	
    protected BaseDao<M, ID> baseDao;
    
    /**
     * 实体类型
     */
    protected Class<M> entityClass;
	
	@PostConstruct
    protected void initConfig(){
		this.entityClass = ReflectUtils.getClassGenricType(getClass());
    	baseDao = this.init();
    }
    protected abstract BaseDao<M, ID> init();
    
    public void setBaseDao(BaseDao<M, ID> baseDao) {
        this.baseDao = baseDao;
    }
    
    public Class<M> getEntityClass() {
		return entityClass;
	}

    /**
     * 保存单个实体
     *
     * @param m
     *            实体
     * @return 返回保存的实体
     */
    public M save(M m) {
        return baseDao.save(m);
    }

    public M saveAndFlush(M m) {
        m = save(m);
        baseDao.flush();
        return m;
    }

    /**
     * 更新单个实体
     *
     * @param m
     *            实体
     * @return 返回更新的实体
     */
    public M update(M m) {
        return baseDao.save(m);
    }

    /**
     * 根据主键删除相应实体
     *
     * @param id
     *            主键
     */
    public void delete(ID id) {
        baseDao.delete(id);
    }

    /**
     * 删除实体
     *
     * @param m
     *            实体
     */
    public void delete(M m) {
        baseDao.delete(m);
    }

    /**
     * 根据主键删除相应实体
     *
     * @param ids
     *            实体
     */
    public void delete(ID[] ids) {
        baseDao.delete(ids);
    }

    /**
     * 按照主键查询
     *
     * @param id
     *            主键
     * @return 返回id对应的实体
     */
    public M findOne(ID id) {
        return baseDao.findOne(id);
    }

    /**
     * 实体是否存在
     *
     * @param id
     *            主键
     * @return 存在 返回true，否则false
     */
    public boolean exists(ID id) {
        return baseDao.exists(id);
    }

    /**
     * 统计实体总数
     *
     * @return 实体总数
     */
    public long count() {
        return baseDao.count();
    }

    /**
     * 查询所有实体
     *
     * @return
     */
    public List<M> findAll() {
        return baseDao.findAll();
    }

    /**
     * 按照顺序查询所有实体
     *
     * @param sort
     * @return
     */
    public List<M> findAll(Sort sort) {
        return baseDao.findAll(sort);
    }

    /**
     * 分页及排序查询实体
     *
     * @param pageable
     *            分页及排序数据
     * @return
     */
    public Page<M> findAll(Pageable pageable) {
        return baseDao.findAll(pageable);
    }

    /**
     * 按条件分页并排序查询实体
     *
     * @param searchable
     *            条件
     * @return
     */
    public Page<M> findAll(Searchable searchable) {
        return baseDao.findAll(searchable);
    }

    /**
     * 按条件不分页不排序查询实体
     *
     * @param searchable
     *            条件
     * @return
     */
    public List<M> findAllWithNoPageNoSort(Searchable searchable) {
        searchable.removePageable();
        searchable.removeSort();
        return Lists.newArrayList(baseDao.findAll(searchable).getContent());
    }

    /**
     * 按条件排序查询实体(不分页)
     *
     * @param searchable
     *            条件
     * @return
     */
    public List<M> findAllWithSort(Searchable searchable) {
        searchable.removePageable();
        return Lists.newArrayList(baseDao.findAll(searchable).getContent());
    }

    /**
     * 按条件分页并排序统计实体数量
     *
     * @param searchable
     *            条件
     * @return
     */
    public Long count(Searchable searchable) {
        return baseDao.count(searchable);
    }

    /**======================eud-common===================**/
    /**
     * 插入单个实体
     */
    public M insert(M m){
    	return baseDao.insert(m);
    }
    
    /**
	 * 根据实体选择性更新,属性为空的便不进行更新
	 * @param m 实体 
	 * @param updateBy 更新条件 多个条件以","分隔,如"billNo,divisionNo";
	 * 		还可带操作符,以"_"分隔,如"billNo,divisionNo_eq",与SearchOperator定义一致,暂不支持in
	 * 		条件的值直接从entity获取;
	 * @return 当前更新行
	 */
	public int updateSelective(M m, String updateBy){
		return baseDao.updateSelective(m, updateBy);
	}
	
	/**
	 * 根据实体id属性进行，支持联合主键属性;选择性更新,属性为空的便不进行更新;除主键属性外的其他属性
	 * @param m 实体 
	 * @param updateByOther 除主键属性外的其他属性;更新条件 多个条件以","分隔,如"billNo,divisionNo";
	 * 		还可带操作符,以"_"分隔,如"billNo,divisionNo_eq",与SearchOperator定义一致,不考虑支持in
	 * 		条件的值直接从entity获取;
	 * 
	 * @return 当前更新行
	 */
	public int updateByIdNames(M m, String updateByOther){
		return baseDao.updateByIdNames(m, updateByOther);
	}
	
	/**
	 * 根据参数条件进行更新，更新内容为传入的entity，不更新的属性则设为null
	 * @param m 更新内容的实体
	 * @param params key支持操作符,如"billNo_lt",默认为eq;
	 * 		下划线后的操作符与SearchOperator定义一致;
	 * 		支持in,value为List对象;
	 */
	public int updateByParams(M m, Map<String, Object> params) {
		return baseDao.updateByParams(m, params);
	}
	
	/**
	 * 将实体的某个属性更新为当前值+1
	 * 应用于【打印次数-printCount】自动+1，【导出次数-outCount】自动+1
	 */
	public int updateForAutoIncrement(String propertyName, Map<String, Object> params) {
		return baseDao.updateForAutoIncrement(propertyName, params);
	}
	
	/**
	 * 根据参数条件进行删除，不支持级联删除
	 * @param params key支持操作符,如"billNo_lt",默认为eq;
	 * 		下划线后的操作符与SearchOperator定义一致;
	 * 		支持in,value为List对象;
	 */
	public int deleteByParams(Map<String, Object> params) {
		return baseDao.deleteByParams(params);
	};
	
	/**
	 * 删除：根据实体id属性进行，支持联合主键属性;除主键属性外的其他属性
	 * @param m 实体 
	 * @param deleteByOther 除主键属性外的其他属性;更新条件 多个条件以","分隔,如"billNo,divisionNo";
	 * 		还可带操作符,以"_"分隔,如"billNo,divisionNo_eq",与SearchOperator定义一致,不考虑支持in
	 * 		条件的值直接从entity获取;
	 * 
	 * @return 当前更新行
	 */
	public int deleteByIdNames(M m, String deleteByOther) {
		return baseDao.deleteByIdNames(m, deleteByOther);
	}
    
    /**
	 * 判断是否为jsonObject，若不是则转换为特定entity
	 * @param m
	 */
	protected M parseBean(Object m, Class<?> calzz){
    	if(m instanceof JSONObject){
			return (M)JSON.parseObject(m.toString(), calzz);
		}else {
			return (M)m;
		}
    }
	
	/**
     * 修改保存前方法，预留给子类覆写，单实体
     * @param entity
     * @param systemUser
     */
    protected boolean beforeUpdate(M entity, SystemUser systemUser){
    	//设置实体默认字段 时间+操作人
		BizUtils.setEntityDefaultField(entity, 1, systemUser, new Date());
		return true;
    }

    /**
     * 新增保存前方法，预留给子类覆写，单实体
     * @param entity 保存的实体
     * @param systemUser 用户
     * @param fieldMap 需额外设置field
     * @return
     */
    protected boolean beforeInsert(M entity, SystemUser systemUser, Map<String, Object> fieldMap){
    	if(null!=fieldMap && fieldMap.size()>0){
    		for(Map.Entry<String, Object> entry : fieldMap.entrySet()){
    			ReflectUtils.invokeSetter(entity, entry.getKey(), entry.getValue());
    		}
    	}
    	BizUtils.setEntityDefaultField(entity, 0, systemUser, new Date());
    	return true;
    }
    
    /**
     * 删除保存前方法，预留给子类覆写，单实体
     * @param entity
     * @param systemUser
     */
    protected boolean beforeDelete(M entity, SystemUser systemUser){
    	
    	return true;
    }
    
    @SuppressWarnings("rawtypes")
	protected BaseService getProxyService(){
		BaseService proxyService = (BaseService)AopContext.currentProxy();
		return proxyService;
	}

    /**
     * 根据参数条件进行更新 以及 自定义的set部分
     * @param entity
     * @param params
     * @param customSetQl  自定义的set部分的Ql，会自动拼接到updateByParams生成的Ql语句的set后面，例如：
     *                    String customSetQl="printCount=printCount+1, creator=null"
     * @return
     */
    public int updateByParamsAndCustomSetQl(M entity,  Map<String, Object> params, String customSetQl){
        return baseDao.updateByParamsAndCustomSetQl(entity, params, customSetQl);
    }

    /**
     * 将实体m脱离实体管理器
     * @param m
     */
    public void detach(M m){
        baseDao.detach(m);
    }
}
